package com.wtf.dsp.admin.core.dao;

import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.hikari.HikariCpConfig;
import com.baomidou.mybatisplus.annotation.DbType;
import com.mysql.jdbc.Driver;

import com.wtf.dsp.admin.core.model.DataSourceInfo;
import com.wtf.dsp.admin.enums.CommonErrorEnum;
import com.wtf.dsp.admin.exception.WtfDspException;
import com.wtf.dsp.admin.model.PageSql;
import com.wtf.dsp.core.constant.NsConstant;
import com.wtf.dsp.core.util.PageUtils;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.ClassUtils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;


public abstract class AbstractJdbcDao implements JdbcDao ,DbConfigDao{

    private JdbcTemplate jdbcTemplate;

    public AbstractJdbcDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public <T> T queryForObject(String sql, Class<T> requiredType) {
        return jdbcTemplate.queryForObject(sql,requiredType);
    }

    @Override
    public List<Map<String, Object>> queryForList(String sql) {
        return jdbcTemplate.queryForList(sql);
    }

    @Override
    public <T> List<T> queryForList(String sql, Class<T> elementType) {
        return jdbcTemplate.queryForList(sql,elementType);
    }

    @Override
    public Map<String, Object> queryForMap(String sql) {
        return jdbcTemplate.queryForMap(sql);
    }

    @Override
    public PageUtils queryForPage(PageSql pageSql) {
        Integer totalCount = queryForObject(pageSql.getTotalSql(),Integer.class);
        List<Map<String,Object>> result = queryForList(pageSql.getExecSql());
        return new PageUtils(result,totalCount,pageSql.getLimit(),pageSql.getPage());
    }

    @Override
    public Connection openConnection(DataSourceInfo dataSourceInfo) throws Exception {
        Connection connection = null;
        try {
            DbType dbType = dataSourceInfo.getDbType();
            Class.forName(dbDriver(dbType.getDb()));
            Properties props = new Properties();
            props.put("remarksReporting", "true");
            props.put("user", dataSourceInfo.getUsername());
            props.put("password", dataSourceInfo.getPassword());
            connection = DriverManager.getConnection(
                    jdbcUrl(dbType.getDb(),dataSourceInfo.getHost(), dataSourceInfo.getPort(), dataSourceInfo.getDbName()), props);
        } catch (ClassNotFoundException e) {
            String errorMsg = "连接创建失败，找不到相关驱动类(" + e.getMessage() + ")";
            throw new WtfDspException(CommonErrorEnum.OPEN_CONNECTION_EXCEPTION.getErrCode(), errorMsg, e);
        } catch (SQLException e) {
            throw e;
        } catch (Exception e) {
            throw new WtfDspException(CommonErrorEnum.OPEN_CONNECTION_EXCEPTION, e);
        }
        return connection;
    }

    protected abstract String dbDriver(String db);

    protected abstract String jdbcUrl(String db, String host, Integer port, String dbName);

    @Override
    public void closeConnection(Connection connection) throws WtfDspException {
        if (connection != null) {
            try {
                connection.close();
                connection = null;
            } catch (SQLException e) {
                throw new WtfDspException(CommonErrorEnum.CLOSE_CONNECTION_EXCEPTION.getErrCode(), e.getMessage(), e);
            }
        }
    }

    public DataSourceProperty getDataSourceProperty(DataSourceInfo dataSourceInfo){
        DbType dbType = dataSourceInfo.getDbType();
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        dataSourceProperty.setPoolName(NsConstant.nsGroupDs(dataSourceInfo.getNs(),dataSourceInfo.getDs()));
        dataSourceProperty.setType(HikariDataSource.class);
        dataSourceProperty.setUrl(jdbcUrl(dbType.getDb(),dataSourceInfo.getHost(), dataSourceInfo.getPort(), dataSourceInfo.getDbName()));
        dataSourceProperty.setDriverClassName(dbDriver(dbType.getDb()));
        dataSourceProperty.setUsername(dataSourceInfo.getUsername());
        dataSourceProperty.setPassword(dataSourceInfo.getPassword());
        HikariCpConfig hikari = new HikariCpConfig();
        hikari.setConnectionTimeout(dataSourceInfo.getConnectionTimeout());
        hikari.setValidationTimeout(dataSourceInfo.getValidationTimeout());
        hikari.setIdleTimeout(dataSourceInfo.getIdleTimeout());
        hikari.setMaxLifetime(dataSourceInfo.getMaxLifetime());
        hikari.setConnectionTestQuery(dataSourceInfo.getConnectionTestQuery());
        hikari.setMinIdle(dataSourceInfo.getMinIdle());
        hikari.setMaxPoolSize(dataSourceInfo.getMaxPoolSize());
        hikari.setLeakDetectionThreshold(dataSourceInfo.getLeakDetectionThreshold());
        dataSourceProperty.setHikari(hikari);
        return dataSourceProperty;
    }

    public Properties getProperties(String jdbcUrl, DbType dbType) throws Exception {
        Properties properties = null;
        switch (dbType){
            case MYSQL:
                Driver mysqlDriver = new Driver();
                properties = mysqlDriver.parseURL(jdbcUrl,null);
                break;
            case POSTGRE_SQL:
                Properties pgProperties = org.postgresql.Driver.parseURL(jdbcUrl, null);
                properties = new Properties();
                properties.put("HOST", pgProperties.getProperty("PGHOST"));
                properties.put("PORT", pgProperties.getProperty("PGPORT"));
                properties.put("DBNAME", pgProperties.getProperty("PGDBNAME"));
                break;
            default:
                break;
        }
        return properties;
    }


    @Override
    public int insertDeleteUpdate(String excSql) {
        return jdbcTemplate.update(excSql);
    }

    @Override
    public boolean checkDriver(String driverClass){
        try {
            Class.forName(driverClass);
            return true;
        } catch (ClassNotFoundException e) {
            throw new WtfDspException(CommonErrorEnum.DB_DRIVER_ERROR);
        } catch (Throwable var4){
            return false;
        }
    }
}
